package com.geneqiao.jdbc.util;

import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import org.apache.log4j.Logger;

import com.geneqiao.jdbc.jpa.DateFormat;
import com.geneqiao.jdbc.jpa.TableStruct;

public class DataUtils
{

	// 日志
	private static final Logger logger = Logger.getLogger(DataUtils.class);

	private static final Object obj1 = new Object();
	private static final Object obj2 = new Object();

	public static String getDateFormat(Date date, String format)
	{
		return new SimpleDateFormat(format).format(date);
	}

	public static <T> T copyValue(Map<Integer, Field> map, ResultSet resultSet, Class<T> t)
	{
		try
		{
			T tt = t.newInstance();
			Set<Integer> keys = map.keySet();
			Field field = null;
			Object obj = null;
			for (Integer columnIndex : keys)
			{
				field = map.get(columnIndex);
				obj = resultSet.getObject(columnIndex);
				synchronized (obj1)
				{
					field.setAccessible(true);
					field.set(tt, ConvertValue(field, t, obj));
					field.setAccessible(false);
				}
			}
			return tt;
		}
		catch (Exception e)
		{
			logger.error("数据库对象转换失败,传入对象 => " + t.getName(), e);
		}
		return null;
	}

	private static Object ConvertValue(Field field, Class<?> t, Object obj) throws IntrospectionException
	{
		Type genericType = field.getGenericType();
		if (obj != null)
		{
			if (genericType == String.class)
			{
				if (obj instanceof Timestamp)
				{
					String format = "yyyy-MM-dd HH:mm:ss";
					PropertyDescriptor pd = new PropertyDescriptor(field.getName(), t);
					Method getMethod = pd.getReadMethod();// 获得get方法
					if (pd != null)
					{
						DateFormat df = getMethod.getAnnotation(DateFormat.class);
						if (df != null)
							format = df.pattern();
					}
					return getDateFormat((Date) obj, format);
				}
				return obj.toString();
			}
			if (!"".equals(obj.toString()))
			{
				if (genericType == Date.class)
					return (Date) obj;
				if (genericType == Integer.class)
					return Integer.valueOf(obj.toString());
				if (genericType == Double.class)
					return Double.valueOf(obj.toString());
				if (genericType == Long.class)
					return Long.valueOf(obj.toString());
				if (genericType == Float.class)
					return Float.valueOf(obj.toString());
				if (genericType == char.class)
					return obj.toString().charAt(0);
				if (genericType == Boolean.class)
					return Boolean.parseBoolean(obj.toString());
			}
			return obj;
		}
		return null;
	}

	public static <T> Map<Integer, Field> buildColumnMap(Class<T> t, ResultSetMetaData metaData)
	{
		Set<Field> fields = getDeclaredFields(t);
		Map<Integer, Field> map = new HashMap<>();
		if (fields == null || fields.isEmpty()) { return map; }
		if (logger.isDebugEnabled())
			logger.debug("Build object map with metadata");
		try
		{
			String fieldName;
			for (Field field : fields)
			{
				fieldName = field.getName();
				for (int i = 1; i < metaData.getColumnCount() + 1; i++)
				{
					if (fieldName.equals(metaData.getColumnLabel(i)) || fieldName.equals(metaData.getColumnName(i))
							|| fieldName.equals(ConvertName(metaData.getColumnLabel(i)))
							|| fieldName.equals(ConvertName(metaData.getColumnName(i))))
					{
						map.put(i, field);
						break;
					}
				}
			}
		}
		catch (SQLException e)
		{
			logger.error("获取对象和数据库的映射失败", e);
		}
		return map;
	}

	public static String ConvertName(String name)
	{
		name = name.toLowerCase();
		String[] strs = name.split("_");
		StringBuffer sb = new StringBuffer();
		for (int i = 0; i < strs.length; i++)
		{
			if (i == 0)
			{
				sb.append(strs[i]);
			}
			else
			{
				char[] c = strs[i].toCharArray();
				for (int j = 0; j < c.length; j++)
				{
					if (j == 0)
					{
						sb.append((c[j] + "").toUpperCase());
					}
					else
					{
						sb.append(c[j]);
					}
				}
			}
		}
		return sb.toString();
	}

	public static void buildSQLMap(TableStruct struct, SQLType sqlType)
	{
		switch (sqlType)
		{
		case INSERT:
			struct.setBuildMap(getInsertSQL(struct));
			break;
		case UPDATE:
			struct.setBuildMap(getUpdateSQL(struct));
			break;
		case UPDATENOTNULL:
			struct.setBuildMap(getUpdateNotNullSQL(struct));
			break;
		case DELETE:
			struct.setBuildMap(getDeleteSQL(struct));
			break;
		default:
			break;
		}
	}

	private static Map<Integer, Object> getDeleteSQL(TableStruct tableStruct)
	{
		Map<Integer, Object> map = new HashMap<Integer, Object>();
		try
		{
			Map<String, Object> columns = tableStruct.getColumns();
			StringBuffer sql1 = new StringBuffer();
			Set<String> keys = columns.keySet();
			int j = 1;
			Object obj = null;
			for (String key : keys)
			{
				obj = columns.get(key);
				if (obj != null)
				{
					sql1.append(key + "=? AND ");
					map.put(j, obj);
					j++;
				}
			}
			if (j > 1)
			{
				sql1.delete(sql1.length() - 5, sql1.length());
			}
			String sql = "delete from " + tableStruct.getTableName() + " where " + sql1.toString();
			if (logger.isDebugEnabled())
			{
				logger.debug("构造完成的SQL => " + sql);
			}
			map.put(0, sql);
		}
		catch (Exception e)
		{
			logger.error("构造DELETE语句失败", e);
		}
		return map;
	}

	private static Map<Integer, Object> getUpdateNotNullSQL(TableStruct tableStruct)
	{
		Map<Integer, Object> map = new HashMap<Integer, Object>();
		try
		{
			Map<String, Object> columns = tableStruct.getColumns();
			String primaryKey = tableStruct.getPrimaryKey();

			StringBuffer sql1 = new StringBuffer(" set ");
			Set<String> keys = columns.keySet();
			int j = 1;
			Object obj = null;
			for (String key : keys)
			{
				obj = columns.get(key);
				if (!key.equals(primaryKey) && obj != null)
				{
					sql1.append(key + "=?,");
					map.put(j, obj);
					j++;
				}
			}
			sql1.deleteCharAt(sql1.length() - 1);
			StringBuffer sql2 = new StringBuffer(primaryKey + " = ?");
			map.put(j, columns.get(primaryKey));
			String sql = "update " + tableStruct.getTableName() + sql1.toString() + " where " + sql2.toString();
			if (logger.isDebugEnabled())
			{
				logger.debug("构造完成的SQL => " + sql);
			}
			map.put(0, sql);
		}
		catch (Exception e)
		{
			logger.error("构造UPDATE语句失败", e);
		}
		return map;
	}

	private static Map<Integer, Object> getUpdateSQL(TableStruct tableStruct)
	{
		Map<Integer, Object> map = new HashMap<Integer, Object>();
		try
		{
			Map<String, Object> columns = tableStruct.getColumns();
			String primaryKey = tableStruct.getPrimaryKey();

			StringBuffer sql1 = new StringBuffer(" set ");
			Set<String> keys = columns.keySet();
			int j = 1;
			for (String key : keys)
			{
				if (!key.equals(primaryKey))
				{
					sql1.append(key + "=?,");
					map.put(j, columns.get(key));
					j++;
				}
			}
			sql1.deleteCharAt(sql1.length() - 1);
			StringBuffer sql2 = new StringBuffer(primaryKey + " = ?");
			map.put(j, columns.get(primaryKey));
			String sql = "update " + tableStruct.getTableName() + sql1.toString() + " where " + sql2.toString();
			if (logger.isDebugEnabled())
			{
				logger.debug("构造完成的SQL => " + sql);
			}
			map.put(0, sql);
		}
		catch (Exception e)
		{
			logger.error("构造UPDATE语句失败", e);
		}
		return map;
	}

	private static Map<Integer, Object> getInsertSQL(TableStruct tableStruct)
	{
		Map<Integer, Object> map = new HashMap<Integer, Object>();
		try
		{
			Map<String, Object> columns = tableStruct.getColumns();
			String generageKey = tableStruct.getGenerateKey();

			StringBuffer sql1 = new StringBuffer("(");
			StringBuffer sql2 = new StringBuffer("(");
			Set<String> keys = columns.keySet();
			int j = 1;
			Object obj = null;
			for (String key : keys)
			{
				obj = columns.get(key);
				if (!key.equals(generageKey) && obj != null)
				{
					sql1.append(key + ",");
					sql2.append("?,");
					map.put(j, obj);
					j++;
				}
			}
			sql1.deleteCharAt(sql1.length() - 1);
			sql1.append(")");
			sql2.deleteCharAt(sql2.length() - 1);
			sql2.append(")");
			String sql = "insert into " + tableStruct.getTableName() + sql1.toString() + " values " + sql2.toString();
			if (logger.isDebugEnabled())
			{
				logger.debug("构造完成的SQL => " + sql);
			}
			map.put(0, sql);
		}
		catch (Exception e)
		{
			logger.error("构造Insert语句失败", e);
		}
		return map;
	}

	public static void setPrimaryKey(Object obj, String memberName, Object value)
	{
		try
		{
			Field field = obj.getClass().getDeclaredField(ConvertName(memberName));
			synchronized (obj2)
			{
				field.setAccessible(true);
				field.set(obj, value);
				field.setAccessible(false);
			}
		}
		catch (Exception e)
		{
			logger.error("对主键赋值失败 => " + memberName, e);
		}
	}

	public static <T> Set<Field> getDeclaredFields(Class<T> t)
	{
		Set<Field> fieldList = new HashSet<>();
		Field[] fields = t.getDeclaredFields();
		for (Field field : fields)
		{
			fieldList.add(field);
		}
		getDeclaredFields(t.getSuperclass(), fieldList);
		return fieldList;
	}

	private static void getDeclaredFields(Class<?> superclass, Set<Field> fieldList)
	{
		if (superclass != null)
		{
			Field[] fields = superclass.getDeclaredFields();
			for (Field field : fields)
			{
				fieldList.add(field);
			}
			getDeclaredFields(superclass.getSuperclass(), fieldList);
		}
	}

	public static Object getValue(Object arg0, Method method)
	{
		Object obj = null;
		try
		{
			obj = method.invoke(arg0);
		}
		catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e)
		{
			logger.error("获取entity的值失败", e);
		}
		if (obj != null && !"".equals(obj) && !"null".equals(obj)) { return obj; }
		return obj;
	}
}
